package com.woniuxy.advice;

import com.woniuxy.annotion.MyLimit;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author yellow Docter
 * @date 2024-09-28
 * @desc 蜗牛创想：www.woniuxy.com
 *
 *     @Aspect 定义切面类
 *         切点   对那个方法进行增强
 *
 *         通知   增强代码 (业务代码 redis 统计判断限流代码 )
 */
@Component
@Slf4j
@Aspect
public class LimitAdvice {

    @Autowired
    private RedisTemplate redisTemplate;

    //切点
    //@Pointcut("execution(* com.woniuxy.controller.*(..))")  //切点表达式 切指定的方法
    @Pointcut("@annotation(com.woniuxy.annotion.MyLimit)")    //切点注解
    public void pc(){}

    //通知
    @Around(value = "pc()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {

        /**
         *   ProceedingJoinPoint   程序连接点
         *        增强的方法
         */
        //01 通过反射 拿到 注解中的值
        //通过连接点可以拿到方法签名 方法签名拿到 Method 对象
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        //拿到 方法对象
        Method method = methodSignature.getMethod();

        ValueOperations ops = redisTemplate.opsForValue();
        String methodName = method.getName();

        //redis中的key的名称
        String key = methodName;
        //判断方法上面是否有 注解
        if (method.isAnnotationPresent(MyLimit.class)){
            //拿到注解
            MyLimit annotation = method.getAnnotation(MyLimit.class);
            int count = annotation.count();
            int time = annotation.time();
//            log.info("拿到值=====>count:{},time:{}",count,time);
            System.out.println("time = " + time);
            System.out.println("count = " + count);

            //判断 这个方法的访问次数  如果是第一次进行记录
            Object object = ops.get(key);
            if (Objects.isNull(object)){
                ops.set(key, 1, time, TimeUnit.MINUTES); //存到redis中设置时间
            }else{
                //不是第一次 ++
                int redisCount = Integer.valueOf(object.toString());
                //redis中的数据 大于 登录 限流的数量 return
                if (redisCount >= count){
                    return "短时间内 访问过于频繁...稍后进行重试....";
                }
                ops.increment(key);
            }
        }
        //02 写业务代码
        Object object = pjp.proceed();
        return object;
    }

}
